package com.vtech.mybatis.baseservice.base;

import com.vtech.mybatis.baseservice.util.PluginUtil;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.OutputUtilities;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.GeneratedKey;
import org.mybatis.generator.internal.util.StringUtility;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * TODO
 * 参考：丁窍 / mybatis-generator-plugins
 *
 * @author houzhiwei
 * @date 2016/11/7 17:41.
 */
public abstract class AbstractBatch
{
    private final String InsertBatchMethodName = "insertBatch";
    private final String DeleteBatchMethodName = "deleteBatch";
    private final String SelectBatchMethodName = "selectByPrimaryKeys";
    protected XmlElement insertBatchElement = new XmlElement("insert"); //$NON-NLS-1$
    protected XmlElement deleteBatchElement = new XmlElement("delete"); //$NON-NLS-1$
    protected XmlElement selectBatchElement = new XmlElement("select"); //$NON-NLS-1$

    /**
     * 生成所有批量方法的sqlmap
     *
     * @param document          xml文档
     * @param introspectedTable 表内容
     * @param context           环境
     */
    public void generateAllBatchSqlMaps(Document document, IntrospectedTable introspectedTable,
                                        Context context)
    {
        //DeleteBatch
        generateDeleteBatchSqlMap(document, introspectedTable, context);
        //InsertBatch
        generateInsertBatchSqlMap(document, introspectedTable, context);
        //SelectByPrimaryKeys
        generatorSelectByPrimaryKeysSqlMap(document, introspectedTable, context);
    }

    /**
     * insert语句前缀
     */
    protected String getColumnsPrefix()
    {
        return "(";
    }

    /**
     * insert语句后缀
     *
     * @return
     */
    protected abstract String getColumnsSuffix();

    /**
     * insert语句values部分前缀
     *
     * @return 前缀
     */
    protected abstract String getValuesPrefix();

    /**
     * insert语句values部分后缀
     *
     * @return
     */
    protected abstract String getValuesSuffix();

    /**
     * foreach语句分隔符
     *
     * @return
     */
    protected abstract String getForeachSeparator();

    //生成批量插入的xml片段
    public void generateInsertBatchSqlMap(Document document, IntrospectedTable introspectedTable,
                                          Context context)
    {
        XmlElement insertBatchEl = insertBatchElement;
        XmlElement foreachEl = new XmlElement("foreach");
        insertBatchEl.addAttribute(new Attribute("id", InsertBatchMethodName)); //$NON-NLS-1$
        insertBatchEl.addAttribute(new Attribute("parameterType", //$NON-NLS-1$
                                                 FullyQualifiedJavaType.getNewListInstance().getFullyQualifiedName()));
        context.getCommentGenerator().addComment(insertBatchEl);

        GeneratedKey gk = introspectedTable.getGeneratedKey();
        if (gk != null) {
            IntrospectedColumn introspectedColumn = introspectedTable.getColumn(gk.getColumn());
            // if the column is null, then it's a configuration error. The
            // warning has already been reported
            if (introspectedColumn != null) {
                if (gk.isJdbcStandard()) {
                    insertBatchEl.addAttribute(new Attribute("useGeneratedKeys", "true")); //$NON-NLS-1$ //$NON-NLS-2$
                    insertBatchEl.addAttribute(new Attribute("keyProperty", introspectedColumn.getJavaProperty())); //$NON-NLS-1$
                } else {
                    insertBatchEl.addElement(getSelectKey(introspectedColumn, gk));
                }
            }
        }
        foreachEl.addAttribute(new Attribute("collection", "list"));
        foreachEl.addAttribute(new Attribute("item", "item"));
        foreachEl.addAttribute(new Attribute("index", "index"));
        foreachEl.addAttribute(new Attribute("separator", getForeachSeparator()));
        foreachEl.addElement(new TextElement(getValuesPrefix()));
        StringBuilder insertClause = new StringBuilder();
        StringBuilder valuesClause = new StringBuilder();

        insertClause.append("INSERT INTO "); //$NON-NLS-1$
        insertClause.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        insertBatchEl.addElement(new TextElement(insertClause.toString() + " " + getColumnsPrefix()));

        insertClause.setLength(0);
        List<String> valuesClauses = new ArrayList<String>();
        Iterator<IntrospectedColumn> iter = introspectedTable.getAllColumns().iterator();
        //insertClause.append("  ");
        //valuesClause.append("  ");
        OutputUtilities.xmlIndent(insertClause, 2);
        OutputUtilities.xmlIndent(valuesClause, 2);
        while (iter.hasNext()) {
            IntrospectedColumn introspectedColumn = iter.next();
            if (introspectedColumn.isIdentity()) {
                // cannot set values on identity fields
                // 不设置主键值（若主键设置为自增，其他列明确指明了的话，数据库自己会处理）
                continue;
            }
            insertClause.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
            valuesClause.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn, "item."));
            if (iter.hasNext()) {
                insertClause.append(", "); //$NON-NLS-1$
                valuesClause.append(", "); //$NON-NLS-1$
            }

            if (valuesClause.length() > 80) {
                insertBatchEl.addElement(new TextElement(insertClause.toString()));
                insertClause.setLength(0);
                OutputUtilities.xmlIndent(insertClause, 2);
                valuesClauses.add(valuesClause.toString());
                valuesClause.setLength(0);
                OutputUtilities.xmlIndent(valuesClause, 2);
            }
        }

        if (StringUtility.stringHasValue(insertClause.toString().trim())) {
            insertBatchEl.addElement(new TextElement(insertClause.toString()));
        }
        insertBatchEl.addElement(new TextElement(getColumnsSuffix()));
        if (StringUtility.stringHasValue(valuesClause.toString().trim())) {
            valuesClauses.add(valuesClause.toString());
        }
        for (String clause : valuesClauses) {
            foreachEl.addElement(new TextElement(clause));
        }
        foreachEl.addElement(new TextElement(getValuesSuffix()));
        insertBatchEl.addElement(foreachEl);
        XmlElement parentElement = document.getRootElement();
        parentElement.addElement(insertBatchEl);

    }

    //生成批量删除的xml片段
    public void generateDeleteBatchSqlMap(Document document, IntrospectedTable introspectedTable,
                                          Context context)
    {
        XmlElement deleteBatchEl = deleteBatchElement;
        XmlElement foreachEl = new XmlElement("foreach");
        deleteBatchEl.addAttribute(new Attribute("id", DeleteBatchMethodName)); //方法id
        deleteBatchEl.addAttribute(new Attribute("parameterType", //$NON-NLS-1$
                                                 FullyQualifiedJavaType.getNewListInstance().getFullyQualifiedName()));
        context.getCommentGenerator().addComment(deleteBatchEl);

        StringBuilder deleteClause = new StringBuilder();

        deleteClause.append("DELETE FROM "); //$NON-NLS-1$
        deleteClause.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());//tablename
        deleteBatchEl.addElement(new TextElement(deleteClause.toString()));

        //ref: org.mybatis.generator.codegen.mybatis3.xmlmapper.elements
        // .DeleteByPrimaryKeyElementGenerator
        boolean and = false;
        for (IntrospectedColumn introspectedColumn : introspectedTable
                .getPrimaryKeyColumns()) {
            deleteClause.setLength(0);
            if (and) {
                deleteClause.append("  and "); //$NON-NLS-1$
            } else {
                deleteClause.append("where "); //$NON-NLS-1$
                and = true;
            }

            deleteClause.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
            deleteClause.append(" IN "); //$NON-NLS-1$
        }
        deleteBatchEl.addElement(new TextElement(deleteClause.toString()));

        deleteClause.setLength(0);
        OutputUtilities.xmlIndent(deleteClause, 2);
       /* <foreach collection="list" index="index" item="item" open="(" separator="," close=")">
        #{item}
        </foreach>*/
        foreachEl.addAttribute(new Attribute("collection", "list"));
        foreachEl.addAttribute(new Attribute("item", "item"));
        foreachEl.addAttribute(new Attribute("index", "index"));
        foreachEl.addAttribute(new Attribute("separator", getForeachSeparator()));
        foreachEl.addAttribute(new Attribute("open", "("));
        foreachEl.addAttribute(new Attribute("close", ")"));
        foreachEl.addElement(new TextElement("#{item}"));
        deleteBatchEl.addElement(foreachEl);
        XmlElement parentElement = document.getRootElement();
        parentElement.addElement(deleteBatchEl);
    }

    //根据id批量查询
    //参考：SimpleSelectAllElementGenerator
    public void generatorSelectByPrimaryKeysSqlMap(Document document, IntrospectedTable introspectedTable,
                                                   Context context)
    {
        XmlElement selectBatchEl = selectBatchElement;
        XmlElement foreachEl = new XmlElement("foreach");
        selectBatchEl.addAttribute(new Attribute("id", SelectBatchMethodName)); //方法id
        selectBatchEl.addAttribute(new Attribute("resultMap", //$NON-NLS-1$
                                                 introspectedTable.getBaseResultMapId()));
        selectBatchEl.addAttribute(new Attribute("parameterType", //$NON-NLS-1$
                                                 FullyQualifiedJavaType.getNewListInstance().getFullyQualifiedName()));
        context.getCommentGenerator().addComment(selectBatchEl);

        StringBuilder selectClause = new StringBuilder();
        selectClause.append("SELECT "); //$NON-NLS-1$

        selectBatchEl.addElement(new TextElement(selectClause.toString()));
        selectClause.setLength(0);
        selectBatchEl.addElement(PluginUtil.getBaseColumnListElement(introspectedTable));

        selectClause.append(" FROM "); //$NON-NLS-1$
        selectClause.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());//tablename
//     单一主键  WHERE [id] IN
        selectBatchEl.addElement(new TextElement(selectClause.toString()));

        boolean and = false;
        for (IntrospectedColumn introspectedColumn : introspectedTable
                .getPrimaryKeyColumns()) {
            selectClause.setLength(0);
            if (and) {
                selectClause.append("  and "); //$NON-NLS-1$
            } else {
                selectClause.append("where "); //$NON-NLS-1$
                and = true;
            }
            selectClause.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
            selectClause.append(" IN "); //$NON-NLS-1$
        }
        selectBatchEl.addElement(new TextElement(selectClause.toString()));
        selectClause.setLength(0);

        OutputUtilities.xmlIndent(selectClause, 2);
       /* <foreach collection="list" index="index" item="item" open="(" separator="," close=")">
        #{item}
        </foreach>*/
        foreachEl.addAttribute(new Attribute("collection", "list"));
        foreachEl.addAttribute(new Attribute("item", "item"));
        foreachEl.addAttribute(new Attribute("index", "index"));
        foreachEl.addAttribute(new Attribute("separator", getForeachSeparator()));
        foreachEl.addAttribute(new Attribute("open", "("));
        foreachEl.addAttribute(new Attribute("close", ")"));
        foreachEl.addElement(new TextElement("#{item}"));
        selectBatchEl.addElement(foreachEl);
        XmlElement parentElement = document.getRootElement();
        parentElement.addElement(selectBatchEl);
    }

    /**
     * This method should return an XmlElement for the select key used to
     * <p/>
     * automatically generate keys.
     *
     * @param introspectedColumn the column related to the select key statement
     * @param generatedKey       the generated key for the current table
     * @return the selectKey element
     */
    protected XmlElement getSelectKey(IntrospectedColumn introspectedColumn,
                                      GeneratedKey generatedKey)
    {
        String identityColumnType = introspectedColumn.getFullyQualifiedJavaType().getFullyQualifiedName();
        XmlElement answer = new XmlElement("selectKey"); //$NON-NLS-1$
        answer.addAttribute(new Attribute("resultType", identityColumnType)); //$NON-NLS-1$
        answer.addAttribute(new Attribute("keyProperty", introspectedColumn.getJavaProperty())); //$NON-NLS-1$
        answer.addAttribute(new Attribute("order", //$NON-NLS-1$
                                          generatedKey.getMyBatis3Order()));
        answer.addElement(new TextElement(generatedKey.getRuntimeSqlStatement()));
        return answer;
    }
}
